package ma.jeetconsulting.pocs.service;

import static ma.jeetconsulting.pocs.service.DependencyAssembler.toModelDependency;

import java.util.ArrayList;
import java.util.List;

import ma.jeetconsulting.pocs.domain.Dependency;
import ma.jeetconsulting.pocs.persistence.AlertResolverRepository;
import ma.jeetconsulting.pocs.persistence.DependencyRepository;

import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.collection.CollectResult;
import org.sonatype.aether.graph.DependencyNode;
import org.sonatype.aether.graph.DependencyVisitor;


public class ConflectsDetectorService {
	
	
	
	private AlertResolverRepository alertResolverRepository;
	private DependencyRepository dependencyRepository;
	private ConflectsChecker conflectsChecker;
	
	/**
	 * 
	 * 
	 */
	public ConflectsDetectorService() {
		conflectsChecker = new DroolsConflectsChecker();
	}
	
	public DependencyRepository getDependencyRepository() {
		return dependencyRepository;
	}

	public void setDependencyRepository(DependencyRepository dependencyRepository) {
		this.dependencyRepository = dependencyRepository;
	}

	public Result verifyDependencyIntegrationInModule(String modulePath, Dependency checkedDependency) {
		List<EventuelConflect>  eventuelConflects = getDependencyConflictsInModule(modulePath, checkedDependency);
		List<EventuelConflect>  notTraitedEventuelConfelct = getDependencyConflictsInModule(modulePath, checkedDependency);
		List<Solution> solutions = new ArrayList<>();
		for(EventuelConflect conflect : eventuelConflects) {
		 	Solution solution = conflectsChecker.getSolutionFor(conflect);
		 	if(solution != null)
				solutions.add(solution);
			else 
				notTraitedEventuelConfelct.add(conflect);
		}
		Result result = new Result();
		result.setNotTraitedEventuelConflict(notTraitedEventuelConfelct);
		result.setSolutions(solutions);
		return result;
	}
	
	

	public List<EventuelConflect> getDependencyConflictsInModule(String modulePath, Dependency checkedDependency) {
		List<Dependency> dependencies = getDependenciesForModuleInPath(modulePath);
		List<EventuelConflect> migrationDependencies = new ArrayList<>();
		for(Dependency moduleDependency : dependencies) {
			if(!moduleDependency.hasSameGroupAndArtifact(checkedDependency))
				migrationDependencies.addAll(getConflictsBetweenTheseTwoDependencies(checkedDependency, moduleDependency));
		}
		return migrationDependencies;
	}

	private List<EventuelConflect> getConflictsBetweenTheseTwoDependencies( Dependency checkedDependency, Dependency moduleDependency) {
		DependencyNode rootOfCheckedDependency = getRootNodeForDependency(checkedDependency);
		DependencyNode rootOfModuleDependency = getRootNodeForDependency(moduleDependency);
		return getDependenciesWithDiffenrentesVersionsIn(rootOfCheckedDependency, rootOfModuleDependency);
	}

	private List<EventuelConflect> getDependenciesWithDiffenrentesVersionsIn(DependencyNode rootOfCheckedDependency,
									DependencyNode rootOfModuleDependency) {
		List<EventuelConflect> resultHolder = new ArrayList<EventuelConflect>();
		rootOfCheckedDependency.accept(makeDependencyTreeVisitor(rootOfModuleDependency, resultHolder));
		return resultHolder;
	}

	private DependencyVisitor makeDependencyTreeVisitor(final DependencyNode rootOfModuleDependency, final List<EventuelConflect> resultHolder) {
		return new DependencyVisitor() {
			
			@Override
			public boolean visitLeave(DependencyNode node) {return true;}
			
			@Override
			public boolean visitEnter(DependencyNode checkedNode) {
				Artifact artifact = checkedNode.getDependency().getArtifact();
				DependencyNode moduleNode = getConflicteArtifactIfThereIsAnyInModuleProject(artifact, rootOfModuleDependency);
				if(moduleNode != null) {
					EventuelConflect migrationDependency = new EventuelConflect(checkedNode, moduleNode);
					resultHolder.add(migrationDependency);
				}
				return true;
			}
		};
	}
	
	private DependencyNode getConflicteArtifactIfThereIsAnyInModuleProject(
			final Artifact checkedArtifact, DependencyNode rootOfModuleDependency) {
		final ArrayList<DependencyNode> hack = new ArrayList<>();
		rootOfModuleDependency.accept(new DependencyVisitor() {
			
			@Override
			public boolean visitLeave(DependencyNode node) {return true;}
			
			@Override
			public boolean visitEnter(DependencyNode node) {
				Artifact moduleArtifact = node.getDependency().getArtifact();
				if(theseArtifactCanCreateConflicts(checkedArtifact, moduleArtifact))
					hack.add(node);
				return true;
			}

		});
		
		if(hack.size() == 0)
			return null;
		return hack.get(0);
	}
	
	private boolean theseArtifactCanCreateConflicts(
			final Artifact checkedArtifact, Artifact moduleArtifact) {
		return moduleArtifact.getGroupId().equals(checkedArtifact.getGroupId()) && moduleArtifact.getArtifactId().equals(checkedArtifact.getArtifactId()) && !moduleArtifact.getVersion().equals(checkedArtifact.getVersion());
	}
	
	private DependencyNode getRootNodeForDependency(Dependency dependency) {
		DependenciesAnalyzer analyzer = new DependenciesAnalyzerImpl();
		CollectResult theDependecyTree = analyzer.getTheDependecyTree(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
		return theDependecyTree.getRoot();
	}
	
	private List<Dependency> getDependenciesForModuleInPath(String modulePath) {
		ModuleAnalyzer moduleAnalyzer = new ModuleAnalyzer();
		return toModelDependency(moduleAnalyzer.getDependencies(modulePath));
	}

	public AlertResolverRepository getAlertResolverRepository() {
		return alertResolverRepository;
	}

	public void setAlertResolverRepository(
			AlertResolverRepository alertResolverRepository) {
		this.alertResolverRepository = alertResolverRepository;
	}

}
